package cn.cachalot.oj.util;

import cn.cachalot.oj.dao.CompileResult;
import cn.cachalot.oj.dao.RunResult;
import cn.cachalot.oj.entity.Submit;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sun.tools.javac.Main;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Value;

import java.io.*;
import java.util.concurrent.TimeUnit;

@Slf4j
public class CompileUtil {


    public static RunResult run(Integer language, String input, String containerId) {
        String path = "/tmp/";
        RunResult runResult = new RunResult();
        runResult.setTimeOut(false);
        String cmd = "";
        switch (language) {
            //java
            case 0:
                cmd = String.format("docker exec -i  %s  java -classpath %s %s", containerId, path, "Main");
                break;
            //C/C++
            case 1:
                cmd = "docker exec -i " + containerId + " " + path + "Main";
                break;
            //python
            case 2:
                cmd = String.format("docker exec -i %s python3  %s", containerId, path + "Main.py");
                break;
            //js
            case 3:
                cmd = String.format("docker exec -i %s node  %s", containerId, path + "Main.js");
                break;
            default:

        }
        try {
            Process process = Runtime.getRuntime().exec(cmd);
            OutputStream stdinTo = process.getOutputStream();
            stdinTo.write(input.getBytes());
            stdinTo.close();
            ProcessOutputThread outputThread = new ProcessOutputThread(process.getInputStream());
            ProcessOutputThread errorOutputThread = new ProcessOutputThread(process.getErrorStream());
            outputThread.start();
            errorOutputThread.start();
            boolean isSuccess = process.waitFor(5, TimeUnit.SECONDS);
            if (!isSuccess) {
                process.destroy();
                runResult.setTimeOut(true);
            }
            outputThread.join();
            errorOutputThread.join();
            runResult.setOutput(outputThread.getOutput());
            runResult.setErrorOutput(errorOutputThread.getOutput());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return runResult;
    }

    public static CompileResult compile(Integer language, String code, String containerId) {
        CompileResult compileResult = new CompileResult();
        String path = "/tmp/";
        File file;
        String cmd;
        Process process;
        String fileName = IdWorker.getIdStr();
        writeFile("./" + fileName + ".tmp", code);
        try {
            switch (language) {
                //java
                case 0:
                    cmd = String.format("docker cp ./" + fileName + ".tmp %s:/tmp/Main.java", containerId);
                    process = Runtime.getRuntime().exec(cmd);
                    process.waitFor();
//                    compileResult.setFilePath(path);
                    cmd = String.format("docker exec  %s javac -encoding utf-8 %s -d %s", containerId,
                            path + "Main.java", path);
                    new File("./" + fileName + ".tmp").delete();
                    break;
                //C/C++
                case 1:
                    cmd = String.format("docker cp ./" + fileName + ".tmp %s:/tmp/Main.cpp", containerId);
                    process = Runtime.getRuntime().exec(cmd);
                    process.waitFor();
//                    compileResult.setFilePath(path);
                    cmd = String.format("docker exec  %s  g++ %s -o %s", containerId, path + "Main.cpp", path + "Main");
                    new File("./" + fileName + ".tmp").delete();
                    break;
                //python
                case 2:
                    cmd = String.format("docker cp ./" + fileName + ".tmp %s:/tmp/Main.py", containerId);
                    process = Runtime.getRuntime().exec(cmd);
                    process.waitFor();
//                    compileResult.setFilePath(path);
                    new File("./" + fileName + ".tmp").delete();
                    return compileResult;
                //js
                case 3:
                    cmd = String.format("docker cp ./" + fileName + ".tmp %s:/tmp/Main.js", containerId);
                    process = Runtime.getRuntime().exec(cmd);
                    process.waitFor();
//                    compileResult.setFilePath(path);
                    new File("./" + fileName + ".tmp").delete();
                    return compileResult;
                default:
                    new File("./" + fileName + ".tmp").delete();
                    compileResult.setErrorOutput("编译语言出错!");
                    return compileResult;
            }
            process = Runtime.getRuntime().exec(cmd);
            StringBuilder outputBuilder = new StringBuilder();
            InputStream stdoutFrom = process.getInputStream();
            while (true) {
                int ch = stdoutFrom.read();
                if (ch == -1) {
                    break;
                }
                outputBuilder.append((char) ch);
            }
            stdoutFrom.close();
            compileResult.setOutput(outputBuilder.toString());
            StringBuilder errorBuilder = new StringBuilder();
            InputStream stderrFrom = process.getErrorStream();
            while (true) {
                int ch = stderrFrom.read();
                if (ch == -1) {
                    break;
                }
                errorBuilder.append((char) ch);
            }
            stderrFrom.close();
            compileResult.setErrorOutput(errorBuilder.toString());
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        return compileResult;
    }

    private static void writeFile(String filePath, String content) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(filePath)) {
            fileOutputStream.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
